#include "CSqlCounter.h"
#include <stack>
#include <vector>
#include <list>
#include <iterator>

CSqlCounter::CSqlCounter()
{
	classtype = SQL;
	language_name = "SQL";
	casesensitive = false;

	file_extension.push_back(".sql");
	QuoteStart = "\"'"; // As per comment in CCodeCounter.h
	QuoteEnd = "\"'"; // As per comment in CCodeCounter.h
	LineCommentStart.push_back("--");

	BlockCommentStart.push_back("/*");
	BlockCommentEnd.push_back("*/");

	//Define data list
	data_name_list.push_back("char");
	data_name_list.push_back("character");
	data_name_list.push_back("varchar");
	data_name_list.push_back("boolean");
	data_name_list.push_back("tinyint");
	data_name_list.push_back("smallint");
	data_name_list.push_back("integer");
	data_name_list.push_back("int");
	data_name_list.push_back("float");
	data_name_list.push_back("real");
	data_name_list.push_back("double");
	data_name_list.push_back("timestamp");
	data_name_list.push_back("time");
	data_name_list.push_back("date");
	data_name_list.push_back("interval");
	data_name_list.push_back("blob");
	data_name_list.push_back("enum");

	//Define executable list
	//Data statements [R01]
	exec_name_list.push_back("select");
	exec_name_list.push_back("update");
	exec_name_list.push_back("insert");
	exec_name_list.push_back("delete");
	exec_name_list.push_back("set");
	exec_name_list.push_back("alter");
	exec_name_list.push_back("declare");
	exec_name_list.push_back("fetch");
	exec_name_list.push_back("close");

	//Schema statements [R02]
	exec_name_list.push_back("create");
	exec_name_list.push_back("replace");
	exec_name_list.push_back("comment");
	exec_name_list.push_back("truncate");
	exec_name_list.push_back("rename");
	exec_name_list.push_back("drop");
	exec_name_list.push_back("grant");
	exec_name_list.push_back("revoke");

	//Transaction statements [R03]
	exec_name_list.push_back("commit");
	exec_name_list.push_back("rollback");

	//Conditional statements [R04]
	exec_name_list.push_back("where");
	exec_name_list.push_back("group by");
	exec_name_list.push_back("order by");
	exec_name_list.push_back("having");
	exec_name_list.push_back("limit");
	exec_name_list.push_back("join");
	exec_name_list.push_back("union");
	exec_name_list.push_back("intersect");
	exec_name_list.push_back("except");
	//end

/*
	//Complexity measures
	cmplx_assign_list.push_back("=");

	cmplx_cond_list.push_back("where");
	cmplx_cond_list.push_back("group by");
	cmplx_cond_list.push_back("order by");
	cmplx_cond_list.push_back("having");
	cmplx_cond_list.push_back("limit");
	cmplx_cond_list.push_back("join");
	cmplx_cond_list.push_back("union");
	cmplx_cond_list.push_back("intersect");
	cmplx_cond_list.push_back("except");

	//cmplx_calc_list
	cmplx_calc_list.push_back("+");
	cmplx_calc_list.push_back("-");
	cmplx_calc_list.push_back("*");
	cmplx_calc_list.push_back("/");
	
	//cmplx_logic_list
	cmplx_logic_list.push_back("=");
	cmplx_logic_list.push_back("<");
	cmplx_logic_list.push_back(">");
	cmplx_logic_list.push_back("and");
	cmplx_logic_list.push_back("or");
	cmplx_logic_list.push_back("xor");
	cmplx_logic_list.push_back("not");

*/
	//log_func_list

	//trig_func_list

	//math_func_list
}

// Method: ReplaceQuote(...)
// Description: handle special case for SQL literal strings
// -----------------------------------------
// Revision  Date     Author  Modification
// Rev-1     03/20/09   VN      Original author
int CSqlCounter::ReplaceQuote(string &strline, size_t &idx_start, bool &contd, char &CurrentQuoteEnd) 
{
	size_t idx = string::npos;
	while (true)
	{
		idx = strline.find("''", idx_start);
		if (idx != string::npos) strline.replace(idx, 2, 2, '$');
		else break;
	}
	while (true)
	{
		idx = strline.find("\"\"", idx_start);
		if (idx != string::npos) strline.replace(idx, 2, 2, '$');
		else break;
	}
	return CCodeCounter::ReplaceQuote(strline, idx_start, contd, CurrentQuoteEnd);
}

int CSqlCounter::CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak)
{
	result->directive_lines[PHY] = 0;
	result->directive_lines[LOG] = 0;
	return 1;
}

// Revision  Date     Author  Modification
// Rev-1     Mar 09   VNG     Updated phy_flag to make sure the number 
//                             of PHYSICAL SLOC counted correctly. 
// Rev-2     Apr 09   VNG	  Fixed the problem when running sql file with multiple '(' and ')'
int CSqlCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak)
{
	bool found;
	size_t i, j, k, ind, nextInd, startInd, endInd, bstart, strSize; //Mod042909AS
	int sloc_count, dsloc_count; //Mod042909AS
	string stmt, stmtBak, sloc_stmt, exec_keyword, data_keyword;
	filemap::iterator fit, fitbak;

	//TG: SLOC count model start
	bool	blank_flag		= false;
	int		data_count		= 0;
	int		exec_count		= 0;
	bool	phy_flag		= false;
	bool	trunc_flag		= false;
	string	exclude			= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$";
	string	line			= "";
	string	lineBak			= "";
	//end

	//TG: grant/revoke handling
	vector<string> grant_list;
	grant_list.push_back("grant");
	grant_list.push_back("revoke");
	size_t grant_count = 0;
	//end

	//TG:Diffing specific model start
	vector<string> nestedSql, nestedSqlBak;
	stack<size_t> bstack;
	list<size_t> slocIndices;
	string sqlContent, sqlContentBak;
	string prev_sloc;
	//end

	for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++)
	{
		// insert blank at the beginning(for searching keywords)
		line = ' ' + fit->line;
		lineBak = ' ' + fitbak->line;

		if (CUtil::CheckBlank(line))
		{
			blank_flag = true;
			continue;
		}
		else
		{
			blank_flag = false;
		}
		phy_flag=true;

		// Count sloc
		if (!blank_flag)
		{
			sqlContent += line;
			sqlContentBak += lineBak;

			// Check for exec PSLOC line				 
			exec_count=0;
			CUtil::CountTally(line, exec_name_list, exec_count, 1, exclude, "", "", &exec_name_count, result->duplicate, false);
			if (0 < exec_count)
			{
				result->exec_lines[PHY]++;
				phy_flag=false;
			}

			// Check data PSLOC
			data_count=0;
			CUtil::CountTally(line, data_name_list, data_count, 1, exclude, "", "", &data_name_count, result->duplicate, false);
			if (0 < data_count && phy_flag)
			{
				result->data_lines[PHY]++;
				phy_flag=false;
			}

			if (phy_flag)
			{
				result->data_lines[PHY]++;
			}
		}
	}

	// Prepare diffing slocs
	// Extract all the embedded sql sloc
	for (i = 0; i < sqlContent.size(); i++)
	{
		if (sqlContent.at(i) == '(')
		{
			bstack.push(i);
		}
		else if (sqlContent.at(i) == ')')
		{
			if (bstack.empty()) continue;
			bstart = bstack.top();
			bstack.pop();
			stmt = sqlContent.substr(bstart, i - bstart + 1);
			stmtBak = sqlContentBak.substr(bstart, i - bstart + 1);

			sloc_count = 0;
			dsloc_count = 0;
			CUtil::CountTally(stmt, exec_name_list, sloc_count, 1, exclude, "", "", NULL, result->duplicate, false);

			if (sloc_count > 0)
			{
				// extract embedded sloc and remove from main content
				nestedSql.push_back(stmt);
				nestedSqlBak.push_back(stmtBak);
//	Removed by VN, 4/6/09 to fix the crashing problem running sql file with multiple ( and )
//				sqlContent.erase(bstart, stmt.length());
//				sqlContentBak.erase(bstart, stmtBak.length());
				sqlContent.replace(bstart, stmt.length(), stmt.length(), ' ');
				sqlContentBak.replace(bstart, stmtBak.length(), stmtBak.length(), ' ');
			}
			else
			{
				CUtil::CountTally(stmt, data_name_list, dsloc_count, 1, exclude, "", "", NULL, result->duplicate, false);
				if (dsloc_count > 0)
				{
					// mark data keywords (not counted as LSLOC)
					for (j = 0; j < data_name_list.size(); j++)
					{
						ind = 0, nextInd = 0;
						data_keyword = data_name_list.at(j);
						while (ind != string::npos)
						{
							ind = CUtil::FindKeyword(stmt, data_keyword, nextInd, TO_END_OF_STRING, false); //Mod042909AS
							if (ind != string::npos)
							{
								stmt.replace(ind, 1, "$");
								sqlContent.replace(bstart + ind, 1, "$");
							}
							nextInd = ind + 1;
						}
					}
				}
				continue;
			}
		}
	}

	// For each statement, extract diffing sloc
	nestedSql.push_back(sqlContent);
	nestedSqlBak.push_back(sqlContentBak);
	sqlContent.clear();
	sqlContentBak.clear();
	for (i = 0; i < nestedSql.size(); i++)
	{
		stmt = nestedSql.at(i);
		stmtBak = nestedSqlBak.at(i);
		if (i != nestedSql.size() - 1)
		{
			stmt = stmt.substr(1, stmt.length() - 2);
			stmtBak = stmtBak.substr(1, stmtBak.length() - 2);
		}
		slocIndices.clear();
		for (j = 0; j < exec_name_list.size(); j++)
		{
			ind = 0, nextInd = 0;
			exec_keyword = exec_name_list.at(j);
			while (ind != string::npos)
			{
				ind = CUtil::FindKeyword(stmt, exec_keyword, nextInd, TO_END_OF_STRING, false);
				if (ind != string::npos)
				{
					slocIndices.push_back(ind);
					if (exec_keyword == "grant" || exec_keyword == "revoke")
					{
						ind += exec_keyword.length() + 1;
						if (ind < stmt.length())
						{
							nextInd = CUtil::FindKeyword(stmt, "on", ind, TO_END_OF_STRING, false);
							if (nextInd < 0)
								nextInd = stmt.length();
							// clear any slocIndices between these values
							for (list<size_t>::iterator it = slocIndices.begin(); it != slocIndices.end(); ++it)
							{
								if (*it >= ind && *it < nextInd)
									*it = INVALID_POSITION; //Mod042909AS
							}
							slocIndices.remove(INVALID_POSITION); //Mod042909AS
							if(nextInd >= stmt.length())
								ind = INVALID_POSITION; //Mod042909AS
							slocIndices.push_back(ind);
						}
						else
							ind = string::npos;
					}
					else
						nextInd = ind + 1;
				}
				else
					nextInd = ind + 1;
			}
		}
		slocIndices.sort();
		slocIndices.unique();
		exec_count = (int)slocIndices.size();
		result->exec_lines[LOG]+= exec_count;
		if (i == nestedSql.size() - 1)
		{
			for (j = 0; j < data_name_list.size(); j++)
			{
				ind = 0, nextInd = 0;
				data_keyword = data_name_list.at(j);
				while (ind != string::npos)
				{
					ind = CUtil::FindKeyword(stmt, data_keyword, nextInd, TO_END_OF_STRING, false); //Mod042909AS
					nextInd = ind + 1;
					if (ind != string::npos)
					{
						// try to get variable name (var name is listed before type in SQL)
						found = false;
						for (k = ind - 1; k >= 0; k--)
						{
							if (stmt[k] != ' ' && stmt[k] != '\t')
								found = true;
							else if (found && (stmt[k] == ' ' || stmt[k] == '\t' || stmt[k] == ','))
							{
								ind = k + 1;
								break;
							}
						}
						if (found && k <= 0)
							ind = 0;
						slocIndices.push_back(ind);
					}
				}
			}
			slocIndices.sort();
			slocIndices.unique();
			data_count = (int)slocIndices.size() - exec_count;
			result->data_lines[LOG]+= data_count;
		}

		while (slocIndices.size() > 1)
		{
			startInd = slocIndices.front();
			slocIndices.pop_front();
			endInd = slocIndices.front();
			strSize = CUtil::TruncateLine(endInd - startInd, 0, result->lsloc_truncate, trunc_flag);
			sloc_stmt = stmtBak.substr(startInd, strSize);
			this->LSLOC(result, sloc_stmt, trunc_flag);
		}
		if (slocIndices.size() > 0)
		{
			startInd = slocIndices.front();
			sloc_stmt = stmtBak.substr(startInd);
			strSize = CUtil::TruncateLine(sloc_stmt.size(), 0, result->lsloc_truncate, trunc_flag);
			if (strSize < sloc_stmt.size())
				sloc_stmt = stmtBak.substr(startInd, strSize);
			this->LSLOC(result, sloc_stmt, trunc_flag);
		}
	}

	return 1;
}

void CSqlCounter::LSLOC(results* result, string line, bool &trunc_flag)
{
	result->addSLOC(line, trunc_flag);
	return;
}

/* int CSqlCounter::LanguageSpecificProcess(filemap* fmap, results* result)
{
	bool blank_flag = false;
	bool comment = false;
	int cnt = 0;
	size_t comPos = string::npos;
	size_t p, pm;
	filemap::iterator fit;
	string l,lm;
	string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$";

	for (fit = fmap->begin(); fit != fmap->end(); fit++)
	{
		l = fit->line;
		lm = fit->line;
		if (comment == true)
		{
			p = l.find("*\/");
			if (p != string::npos)
			{
				l.replace(0, p + 2, " ");
				comment = false;
			}
			else continue;
		}

		comPos = l.find("/*");
		if (comPos == string::npos)
		{
			comment = false;
			comPos = string::npos;
		}
		else
		{
			comment = true;
			p = l.substr(comPos + 3).find("*\/");
			if (p != string::npos)
			{
				l.replace(comPos, p - comPos + 1, " ");
				comment = false;
			}
			else
			{
				l.replace(comPos, l.size() - comPos, " ");
			}
		}

		if (CUtil::CheckBlank(l))
		{
			blank_flag = true;
			continue;
		}
		else
		{
			blank_flag = false;
		}
		// pm = lm.find(";");
		
		result->exec_lines[PHY]++;
		int dcnt = 0;

		cnt = 0;
		char *cstr, *p,* next_token2;

		// Please split this phrase into tokens;

		// Added ID: REP12050802
#ifdef WIN32
		strcpy_s (cstr,(l.size()+1),l.c_str());
#else
		strcpy (cstr,l.c_str());
#endif
		// Added ID: REP12050802

		// cstr now contains a c-string copy of str
		int jcnt=0;
		// Added ID: REP12050803
#ifdef WIN32
		p=strtok_s (cstr," ",&next_token2);
#else
		p=strtok (cstr," ");
#endif
		// Added ID: REP12050803
		while (p != NULL)
		{
			if (strcmp(p, "into") == 0 || strcmp(p, "INTO") == 0 || strcmp(p, "values") == 0 || strcmp(p, "VALUES") == 0 ||
				strcmp(p, "end") == 0 || strcmp(p, "END") == 0 || strcmp(p, "from") == 0 || strcmp(p,"FROM") == 0 ||
				strcmp(p, "table") == 0 || strcmp(p, "TABLE") == 0 || strcmp(p, "and") == 0 || strcmp(p, "AND") == 0 ||
				strcmp(p, "in") == 0 || strcmp(p, "IN") == 0 || strcmp(p, "between") == 0 || strcmp(p, "BETWEEN") == 0 ||
				strcmp(p, "not") == 0 || strcmp(p, "NOT") == 0 || strcmp(p, "key") == 0 || strcmp(p, "KEY") == 0 ||
				strcmp(p, "primary") == 0 || strcmp(p, "PRIMARY") == 0 || strcmp(p, "as") == 0 || strcmp(p, "AS") == 0 ||
				strcmp(p, "with") == 0 || strcmp(p, "WITH") == 0 || strcmp(p, "drop") == 0 || strcmp(p, "DROP") == 0 ||
				strcmp(p, "by") == 0 || strcmp(p, "BY") == 0 || strcmp(p, "count") == 0 || strcmp(p, "COUNT") == 0 ||
				strcmp(p, "sum") == 0 || strcmp(p, "SUM") == 0 || strcmp(p, "right") == 0 || strcmp(p, "RIGHT") == 0 ||
				strcmp(p, "left") == 0 || strcmp(p, "LEFT") == 0 || strcmp(p, "OUTER") == 0 || strcmp (p, "outer") == 0 ||
				strcmp(p, "like") == 0 || strcmp(p, "LIKE") == 0 || strcmp(p, "not") == 0 || strcmp (p, "NOT") == 0 ||
				strcmp (p, "null") == 0 || strcmp(p, "null") == 0 || strcmp(p, "on") == 0 || strcmp (p, "ON") == 0 ||
				strcmp(p, "is") == 0 || strcmp(p, "IS") == 0 || strcmp(p, "begin") == 0 || strcmp(p, "BEGIN") == 0 ||
				strcmp(p, "add") == 0 || strcmp (p, "ADD") == 0 || strcmp(p, "view") == 0 || strcmp(p, "VIEW") == 0 ||
				strcmp(p, "count") == 0 || strcmp(p, "COUNT") == 0 || strcmp(p, "public") == 0 || strcmp(p, "PUBLIC") == 0 ||
				strcmp(p, "ASC") == 0 || strcmp(p, "asc") == 0 || strcmp(p, "dsc") == 0 || strcmp(p, "DSC") == 0)
			{
				jcnt++;
				// cout << "in there...\n";
			}
			// Added ID: REP12050804
#ifdef WIN32
			p=strtok_s(NULL," ",&next_token2);
#else
			p=strtok(NULL," ");
#endif
			// Added ID: REP12050804
		}

		delete[] cstr;

		CUtil::CountTally(l, exec_name_list, cnt, 1, exclude, "", "", &exec_name_count, result->duplicate, false);
		pm = lm.find("GRANT");
		if (pm != string::npos)
		{
			cnt=1;
			jcnt=0;
		}
		pm = lm.find("REVOKE");
		if (pm != string::npos)
		{
			cnt=1;
			jcnt=0;
		}

		result->exec_lines[LOG] += cnt - jcnt;
		CUtil::CountTally(l, data_name_list, dcnt, 1, exclude, "", "", &data_name_count, result->duplicate);

		result->data_lines[LOG] = 0;
		result->data_lines[PHY] = 0;
	}

	return 1;
}
*/
